﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace MadProps
{
    public abstract class BindableModel : PropModelOwner,
        INotifyPropertyChanged, ICustomTypeDescriptor
    {
        protected BindableModel()
            : this(Composition.Default())
        {
        }

        protected BindableModel(Composition composition)
            : base(composition)
        {
            foreach (var prop in this.GetProps())
            {
                prop.PropChanged += prop_PropChanged;
            }
        }

        void prop_PropChanged(object sender, PropChangedEventArgs e)
        {
            propertyChanged.Raise(this, Meta.PropertyName.Of(e.Prop));
        }

        PropertyChangedEventHandler propertyChanged;
        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add { propertyChanged += value; }
            remove { propertyChanged -= value; }
        }

        AttributeCollection ICustomTypeDescriptor.GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this);
        }

        string ICustomTypeDescriptor.GetClassName()
        {
            return TypeDescriptor.GetClassName(this);
        }

        string ICustomTypeDescriptor.GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this);
        }

        TypeConverter ICustomTypeDescriptor.GetConverter()
        {
            return TypeDescriptor.GetConverter(this);
        }

        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this);
        }

        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this);
        }

        object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return TypeDescriptor.GetEvents(this);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
        {
            return TypeDescriptor.GetProperties(this, attributes);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            var clrProps = TypeDescriptor.GetProperties(this, true)
                .Cast<PropertyDescriptor>()
                .Where(pd => !typeof(IProp).IsAssignableFrom(pd.PropertyType));

            var props = this.GetProps()
                .Select(p => new TypeDescription.PropDescriptor(p))
                .Cast<PropertyDescriptor>();

            Func<PropModel, string> GetName = pm => "_" + Meta.PropertyName.Of(pm);

            var propModels = this.GetPropModels()
                .Select(pm => new TypeDescription.PropModelDescriptor(pm, GetName(pm)))
                .Cast<PropertyDescriptor>();

            var array = clrProps.Union(props).Union(propModels).ToArray();
            return new PropertyDescriptorCollection(array);
        }

        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }
    }
}
